package org.wikipedia.miner.util;

import java.sql.SQLException;
import java.util.ArrayList;

import org.wikipedia.miner.model.Article;
import org.wikipedia.miner.model.Category;
import org.wikipedia.miner.model.Page;
import org.wikipedia.miner.model.Redirect;
import org.wikipedia.miner.model.Wikipedia;

public class OldWikiMinerDB extends WikiDB {
	public static Wikipedia wikipedia;

	public OldWikiMinerDB() throws Exception {
		// WikipediaConfiguration conf = new WikipediaConfiguration(new
		// File(wikiconfig)) ;
		System.out.println("starting wikipedia DB...");
		// wikipedia = new Wikipedia(conf, false) ;
		wikipedia = new Wikipedia("192.168.0.210", "wikidb09", "root", "root");
		System.out.println("retrieving articles ...");
	}

	public static void main(String[] args) throws Exception {
		OldWikiMinerDB odb = new OldWikiMinerDB();

		// System.out.println(odb.getParentCategoryTitles("Mathematics",
		// PageType.ARTICLE));
		// System.out.println(odb.getTotalLinksInCount("Mathematics"));
		// System.out.println(odb.getRedirects("Mathematics"));

		// System.out.println(odb.getChildArticles("Physics"));
		// System.out.println(odb
		// .getDescendentArticleTitles("Computer science", 1));
		System.out.println(odb.getDescendentCategoryTitles("Computer science",
				2));
		System.out.println(odb.getDistinctDescendentArticles("Political science", 2));
		// System.out.println(odb.getDistinctDescendentArticles("Mathematics",
		// 0));
		// System.out.println(odb.getFirstParagraphMarkup("Mathematics"));

		// OK
		// System.out.println(odb.getLinksIn("Calculation"));
		// System.out.println(odb.getPageId("Mathematics", PageType.CATEGORY));
		// System.out.println(odb.getTotalArticleCount());
		// System.out.println(odb.getCategoryLinksIn("Mathematics", 0));
		// System.out.println(odb.getCategoryLinksOut("Mathematics", 0));
		// System.out.println(odb.getArticleById(239121));
		// System.out.println(odb.getArticleTitleByTitle("astronomy"));
		// System.out.println(odb.getArticleLinksIn("Calculation"));
		// System.out.println(odb.getArticleLinksOut("Mathematics", 3));

	}

	/**
	 * 
	 * @param categoryTitle
	 * @param depth
	 * @return
	 */
	public ArrayList<Category> getCategorys(String categoryTitle, int depth) {
		if (depth <= 0) {
			System.out.println("depth must be positive!");
			return null;
		}
		// all Category
		ArrayList<Category> ret = new ArrayList<Category>();
		Category category = wikipedia.getCategoryByTitle(categoryTitle);
		if (category == null)
			return null;
		// ����һ����Ŀ¼
		ArrayList<Category> temp = new ArrayList<Category>();

		ArrayList<Category> tl = new ArrayList<Category>();
		ret.add(category);
		temp.addAll(ret);
		try {
			for (int i = 1; i < depth; i++) {
				for (Category c : temp) {
					for (Category ca : c.getChildCategories()) {
						if (!ret.contains(ca)) {
							ret.add(ca);
						}
					}
					tl.addAll(c.getChildCategories());
				}
				temp = tl;
				tl = new ArrayList<Category>();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return ret;
	}

	/**
	 * get the total links out from a given category. It first get all
	 * categories under a given category within a given depth. Then calculate
	 * the sum of all links out from each category. return the Id of links out
	 * 
	 * @param categoryTitle
	 * @param depth
	 * @return
	 */
	public ArrayList<Integer> getCategoryLinksOut(String categoryTitle,
			int depth) {

		// all Category
		ArrayList<Category> cl = new ArrayList<Category>();

		ArrayList<Integer> list = new ArrayList<Integer>();
		try {
			cl = getCategorys(categoryTitle, depth);
			if (cl == null || cl.size() == 0) {
				return null;
			}
			for (Category c : cl) {
				for (Article a : c.getChildArticles()) {
					for (Integer i : a.getLinksOutIds()) {
						if (list.contains(i))
							continue;
						list.add(i);
					}
				}
			}
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * given a category, and certain depth, return IDs of all distinct
	 * descendent articles
	 * 
	 * @param categoryTitle
	 * @param depth
	 * @return
	 */
	public ArrayList<Integer> getDistinctDescendentArticles(
			String categoryTitle, int depth) {

		ArrayList<Integer> list = new ArrayList<Integer>();
		// all Category
		ArrayList<Category> cl = new ArrayList<Category>();

		try {
			cl = getCategorys(categoryTitle, depth);
			if (cl == null || cl.size() == 0) {
				return null;
			}
			for (Category c : cl) {
				for (Article a : c.getChildArticles()) {
					if (!list.contains(a.getId()))
						list.add(a.getId());
				}
			}
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * given a category, and certain depth, return titles of all distinct
	 * descendent articles
	 * 
	 * @param categoryTitle
	 * @param depth
	 * @return
	 */
	public ArrayList<String> getDescendentArticleTitles(String categoryTitle,
			int depth) {

		ArrayList<String> list = new ArrayList<String>();
		// all Category
		ArrayList<Category> cl = new ArrayList<Category>();

		try {
			cl = getCategorys(categoryTitle, depth);
			if (cl == null || cl.size() == 0) {
				return null;
			}
			for (Category c : cl) {
				for (Article a : c.getChildArticles()) {
					if (!list.contains(a.getTitle()))
						list.add(a.getTitle());
				}
			}
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * given a category, and certain depth, return titles of all distinct
	 * descendent categories
	 * 
	 * @param categoryTitle
	 * @param depth
	 * @return
	 */
	public ArrayList<String> getDescendentCategoryTitles(String categoryTitle,
			int depth) {

		ArrayList<String> list = new ArrayList<String>();
		// all Category
		ArrayList<Category> cl = new ArrayList<Category>();

		try {
			cl = getCategorys(categoryTitle, depth);
			if (cl == null || cl.size() == 0) {
				return null;
			}
			for (Category c : cl) {
				if (!list.contains(c.getTitle()))
					list.add(c.getTitle());
			}
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * given a page title and pagetype such as Article, Category etc) return the
	 * ID of the page
	 * 
	 * @param pageTitle
	 * @param pt
	 * @return
	 */
	public int getPageId(String pageTitle, PageType pt) {
		Page p = null;
		if (pt == PageType.ARTICLE) {
			p = wikipedia.getArticleByTitle(pageTitle);
		} else {
			p = wikipedia.getCategoryByTitle(pageTitle);
		}
		if (p == null)
			return -1;
		return p.getId();
	}

	/**
	 * It returns a normalized name of a given article. The given articleTitle
	 * could be a redirect, if it is, return the targetted article title. When
	 * underlying db used is wikipedia miner, it invokes getArticleByTitle which
	 * resolves redirection automatically
	 * 
	 * @param articleTitles
	 * @return
	 */
	public String getArticleTitleByTitle(String articleTitle) {
		Article a = wikipedia.getArticleByTitle(articleTitle);
		if (a == null)
			return null;
		return a.getTitle();
	}

	/**
	 * given an article id, return the title of the article
	 * 
	 * @param id
	 * @return
	 * @throws Exception
	 */
	public String getArticleById(int id) {
		Page a;
		try {
			a = wikipedia.getPageById(id);
			if (a == null)
				return null;
			return a.getTitle();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}

	/*
	 * given an article title, it returns the IDs of all redirects to this
	 * article.
	 */
	public ArrayList<Integer> getRedirects(String articleTitle) {
		Article a = wikipedia.getArticleByTitle(articleTitle);
		SortedVector<Redirect> directings;
		try {
			directings = a.getRedirects();
			if (directings == null)
				return null;
			ArrayList<Integer> ret = new ArrayList<Integer>();
			for (Redirect r : directings) {
				if (ret.contains(r.getId()))
					continue;
				ret.add(r.getId());
			}
			return ret;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * Given the title of the article, return the titles of all articles that
	 * link out from this article
	 * 
	 * @param artTitle
	 * @param step
	 * @return
	 */
	public ArrayList<String> getArticleLinksOut(String artTitle, int step) {
		Article art = wikipedia.getArticleByTitle(artTitle);
		ArrayList<String> tl = new ArrayList<String>();
		try {
			getLinksOut(art, step, tl);
			return tl;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}

	void getLinksOut(Article article, int steps, ArrayList<String> links)
			throws SQLException {
		if (article == null || steps <= 0)
			return;
		SortedVector<Article> lo = article.getLinksOut();
		if (lo == null || lo.size() == 0)
			return;

		for (Article a : lo) {
			String at = a.getTitle();
			if (links.contains(at))
				continue;
			links.add(at);
			getLinksOut(a, steps - 1, links);
		}
	}

	/**
	 * return titles of all articles that link into this given article
	 * 
	 * @param articleTitle
	 * @return
	 */
	public ArrayList<String> getLinksIn(String articleTitle) {
		Article a = wikipedia.getArticleByTitle(articleTitle);
		if (a == null)
			return null;
		SortedVector<Article> al;
		try {
			al = a.getLinksIn();
			if (al == null || al.size() == 0)
				return null;

			ArrayList<String> ret = new ArrayList<String>(al.size());
			for (Article art : al) {
				if (ret.contains(art.getTitle()))
					continue;
				ret.add(art.getTitle());
			}
			return ret;
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {

		}
		return null;
	}

	/**
	 * returns IDs of all articles that link into the given article
	 * 
	 * @param articleTitle
	 * @return
	 */
	public ArrayList<Integer> getArticleLinksIn(String articleTitle) {
		Article a = wikipedia.getArticleByTitle(articleTitle);
		if (a == null)
			return null;
		SortedVector<Article> al;
		try {
			al = a.getLinksIn();
			ArrayList<Integer> ret = new ArrayList<Integer>(al.size());
			for (Article art : al) {
				if (ret.contains(art.getId()))
					continue;
				ret.add(art.getId());
			}
			return ret;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * given a category, first get the set of its subcategories under certain
	 * depth, and then get all the links that link into the set
	 * 
	 * 
	 */
	public ArrayList<Integer> getCategoryLinksIn(String categoryTitle, int depth) {
		ArrayList<Integer> ret = new ArrayList<Integer>();
		ArrayList<Category> cl = new ArrayList<Category>();
		cl = getCategorys(categoryTitle, depth);
		if (cl == null || cl.size() == 0) {
			return null;
		}
		try {
			for (Category c : cl) {
				for (Article a : c.getChildArticles()) {
					for (Integer i : a.getLinksInIds()) {
						if (ret.contains(i))
							continue;
						ret.add(i);
					}
				}
			}
			return ret;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * given article title, calculate the total links into the article.
	 * 
	 * @param articleTitle
	 * @return
	 */
	public int getTotalLinksInCount(String articleTitle) {
		Article art = wikipedia.getArticleByTitle(articleTitle);
		if (art == null) {
			return -1;
		}
		try {
			return art.getLinksInCount();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return 0;
	}

	/**
	 * given a category, return all the immediate child articles of this
	 * category
	 * 
	 * @param categoryTitle
	 * @return
	 */
	public ArrayList<Integer> getChildArticles(String categoryTitle) {
		Category cat = wikipedia.getCategoryByTitle(categoryTitle);
		SortedVector<Article> al;
		try {
			al = cat.getChildArticles();
			ArrayList<Integer> ret = new ArrayList<Integer>(al.size());
			for (Article a : al) {
				if (ret.contains(a.getId()))
					continue;
				ret.add(a.getId());
			}
			return ret;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * given an article, return all the immediate parent categories
	 * 
	 * @param articleTitle
	 * @param pt
	 * @return
	 */
	public ArrayList<String> getParentCategoryTitles(String articleTitle,
			PageType pt) {
		try {
			SortedVector<Category> cl = null;
			if (pt == PageType.ARTICLE) {
				Article a = wikipedia.getArticleByTitle(articleTitle);
				if (a != null)
					cl = a.getParentCategories();
			} else {
				Category c = wikipedia.getCategoryByTitle(articleTitle);
				if (c != null)
					cl = c.getParentCategories();
			}
			if (cl == null || cl.size() == 0)
				return null;
			ArrayList<String> ret = new ArrayList<String>(cl.size());
			for (Category c : cl) {
				if (ret.contains(c.getTitle()))
					continue;
				ret.add(c.getTitle());
			}
			return ret;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * return the count of the total articles in the database
	 * 
	 * @return
	 */
	public long getTotalArticleCount() {
		return wikipedia.getDatabase().getArticleCount();
	}

	@Override
	public void close() {
		try {
			wikipedia.getDatabase().close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Returns the first paragraph of the article. Currently it is not
	 * implemented, so just returns the null
	 * 
	 * @param articleTitle
	 * @return
	 */
	public String getFirstParagraphMarkup(String articleTitle) {
		Article art = wikipedia.getArticleByTitle(articleTitle);
		try {
			return art.getFirstParagraph();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}

}
